/*  Controller.Java
 * 
 *  WebCrawler Program
 *  @author Amandeep Jhajj and Craig Markham
 *  @date 26Apr2012
 */

package Crawler;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;



public class Controller extends Observable implements Runnable, Observer{

	
	public static final int MAX_LINKS = 50;
	
	private final int my_max_total_threads;
	
	private final BlockingQueue<String> my_links;
	
	private final BlockingQueue<List<String>> the_pages;
	
	private final Set<String> the_visited_links;
	
	private final Set<String> my_words;
	
	private final DataGatherer the_dataGatherer;
	
	private int the_total_retrieve_threads;

	private int the_total_parse_threads;
	
	private final String the_seed;
	
	private int the_max_current_threads;
	
	public Controller(final UI the_ui, final String the_seed, final Set<String> the_words,
		      final int the_threads){
		this.the_seed = the_seed;
		
		my_max_total_threads = the_threads;
		
		my_links = new LinkedBlockingQueue<String>();
		the_pages = new LinkedBlockingQueue<List<String>>();
		
		the_visited_links = Collections.synchronizedSet(new HashSet<String>());
		my_words = Collections.synchronizedSet(new HashSet<String>(the_words));
		
		the_dataGatherer = new DataGatherer(the_ui);
		
		the_total_retrieve_threads = 0;
		the_total_parse_threads = 0;
		
		the_visited_links.add("http://questioneverything.typepad.com/"); //One exception.
		
	}

	
	@Override
	public void run() 
	{
		//System.out.println("controller.run ran");

		my_links.add(the_seed);
		the_visited_links.add(the_seed);
		spawnRetriever();
	}
	
	
	
	private synchronized void spawnParser()
	{
		//System.out.println("the_pages:" + the_pages.size());
		while (the_total_retrieve_threads + the_total_parse_threads <= my_max_total_threads && !the_pages.isEmpty() && the_dataGatherer.getCurrPage() < MAX_LINKS)
		{	
			//System.out.println("spawnParser try ran");

			final PageParser worker = new PageParser(my_links, the_visited_links, my_words, the_dataGatherer, the_pages.poll());
			worker.addObserver(this);
			new Thread(worker).start();
			the_total_parse_threads++;
		}
	}
	private synchronized void spawnRetriever()
	{
		//System.out.println("spawnRetriever ran");
		//System.out.println("my_links:" + my_links.size());

		while(the_total_retrieve_threads + the_total_parse_threads <= my_max_total_threads &&
				!my_links.isEmpty() && the_dataGatherer.getCurrPage() < MAX_LINKS)
		{
			try
			{
				//System.out.println("spawnRetriever try ran");

				String url = my_links.poll();
				final PageRetriever worker = new PageRetriever(new URL(url), the_pages);
				worker.addObserver(this);
				new Thread(worker).start();
				the_total_retrieve_threads++;
			}
			catch (MalformedURLException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void update(final Observable caller, final Object info)
	{	
		//System.out.println("controller.update run");

		int the_current_threads = the_total_parse_threads + the_total_retrieve_threads - 1;
		//System.out.println("# Current threads:" + the_current_threads);
		if (caller.getClass().equals(PageParser.class))
		{
			the_total_parse_threads--;
			the_max_current_threads = the_max_current_threads < the_current_threads 
			? the_current_threads : the_max_current_threads;
			spawnRetriever();
			spawnParser();
		}
		else if (caller.getClass().equals(PageRetriever.class))
		{
			//System.out.println("PageRetriever IF statement");
			the_total_retrieve_threads--;
			the_max_current_threads = the_max_current_threads < the_current_threads 
			? the_current_threads : the_max_current_threads;
			spawnParser();
			spawnRetriever();
		}
		
		if ((my_links.size() == 0 && the_pages.size() == 0 &&
				the_total_retrieve_threads == 0 && the_total_parse_threads == 0) ||
				the_dataGatherer.getCurrPage() >= MAX_LINKS)
			{
				System.gc();
				System.gc();
				System.gc();
				this.setChanged();
				this.notifyObservers(the_max_current_threads);
				return;
			}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
